package my.study.demo.proxy.jhlproxy;


import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class JHLProxy {
    public static final String ln = "\r\n";//换行

    protected JHLInvocationHandler h;

    public static Object newProxyInstance(JHLClassLoader loader,
                                          Class<?>[] interfaces,
                                          JHLInvocationHandler h) throws Exception {
        //获取$Proxy0类找到代理对象过程

        //1.动态生成源码 .java文件
        String src = generateSrc(interfaces);
        System.out.println("=========-----------");
        System.out.println(src);
        System.out.println("=========-----------");
        //2.java文件输出到磁盘，保存为文件$Proxy0.java
        String filePath = JHLProxy.class.getResource("").getPath();
        File f  = new File(filePath + "$Proxy0.java");
        FileWriter fw = new FileWriter(f);
        fw.write(src);
        fw.flush();
        fw.close();

        //3.把java文件编译成$Proxy0.class文件
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
        Iterable iterable = manager.getJavaFileObjects(f);
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager,null,null,null, iterable);
        task.call();
        manager.close();

        //4.把生成的.class文件加载到jvm中。
        Class proxyClass = loader.findClass("$Proxy0");
        Constructor c = proxyClass.getConstructor(JHLInvocationHandler.class);

        //5.返回新的代理对象
        Object obj = c.newInstance(h);
        return obj;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuilder sb = new StringBuilder();

        //1、package
        sb.append(JHLProxy.class.getPackage().toString()).append(";").append(ln);

        //2、导入包
        sb.append("import java.lang.reflect.*;").append(ln);

        //3、类开头定义
        sb.append("public final class $Proxy0 implements ");

        //4、自定义函数添加上实现接口
        generateInterfaces(sb, interfaces);

        //5、JHLInvocationHandler
        generateMainContent(sb, interfaces);



        //最后一行的括号
        sb.append(ln).append("}");

        return sb.toString();
    }

    private static void generateMainContent(StringBuilder sb, Class<?>[] interfaces) {
        sb.append("JHLInvocationHandler h;").append(ln);
        sb.append("public $Proxy0(JHLInvocationHandler h) { ").append(ln);
        sb.append("this.h = h;").append(ln);
        sb.append("}").append(ln);

        //这里仅仅是实现第一个接口的所有方法
        for(Method m : interfaces[0].getMethods()) {
            Class<?> [] params = m.getParameterTypes();

//            StringBuilder paramaNames = new StringBuilder();
//            StringBuilder paramValues = new StringBuilder();
//            StringBuilder paramClasses = new StringBuilder();
//
//            for(int i = 0; i < params.length; i ++) {
//                Class clazz = params[i];
//                String type = clazz.getName();
//                String value = clazz.get
//            }

            //先只考虑无参
            sb.append("public ").append(m.getReturnType().getSimpleName()).append(" ").append(m.getName()).append("() {").append(ln);
            sb.append("try { ").append(ln);
            sb.append("Method m = ").append(interfaces[0].getName()).append(".class.getMethod(\"").append(m.getName()).append("\",new Class[]{});").append(ln);
            sb.append("this.h.invoke(this,m,new Object[]{});").append(ln);
            sb.append("}catch(Throwable throwable) {").append(ln);
            sb.append("throw new UndeclaredThrowableException(throwable);").append(ln);
            sb.append("}").append(ln);
            sb.append("}").append(ln);
            System.out.println(sb.toString());


//            sb.append("}").append(ln);
        }
    }

    private static void generateInterfaces(StringBuilder sb, Class<?>[] interfaces) {
        for(Class<?> clazz : interfaces) {
            sb.append(clazz.getSimpleName()).append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append(" {").append(ln);
    }
}
